/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.github.thorqin.toolkit.utility;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author nuo.qin
 */
public final class StringUtils {
	private static final Pattern INT_PATTERN =
			Pattern.compile("^[+-]?\\d+$");
	private static final Pattern DOUBLE_PATTERN =
			Pattern.compile("^[+-]?\\d*\\.\\d+$");


	public interface ReplaceHandler {
		String replace(String target, String[] groups);
	}

	public static String replaceAll(String source, String regex, ReplaceHandler handler) {
		if (source == null || regex == null || handler == null) {
			return source;
		}
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(source);
		StringBuffer buffer = new StringBuffer(source.length());
		int pos = 0;
		while (matcher.find()) {
			buffer.append(source.substring(pos, matcher.start()));
			int count = matcher.groupCount();
			String[] groups = new String[count];
			for (int i = 0; i < count; i++) {
				groups[i] = matcher.group(i + 1);
			}
			buffer.append(handler.replace(matcher.group(), groups));
			pos = matcher.end();
		}
		buffer.append(source.substring(pos));
		return buffer.toString();
	}

	public static boolean isInteger(String text) {
		return INT_PATTERN.matcher(text).find();
	}

	public static boolean isDouble(String text) {
		return DOUBLE_PATTERN.matcher(text).find();
	}

	public static boolean isNumber(String text) {
		return isInteger(text) || isDouble(text);
	}

	public static Set<String> toStringSet(String value) {
		return toStringSet(value, ",");
	}

	public static Set<String> toStringSet(String value, String delimiter) {
		Set<String> result = new HashSet<>();
		if (value == null)
			return result;
		String[] array = value.split(delimiter);
		for (String v: array)
			if (v != null && !v.isEmpty())
				result.add(v);
		return result;
	}

	public static String join(Iterable<String> array) {
		return join(array, ",");
	}

	public static String join(Iterable<String> array, String delimiter) {
		if (array == null)
			return "";
		StringBuilder result = new StringBuilder();
		Iterator<String> it = array.iterator();
		boolean first = true;
		while (it.hasNext()) {
			if (first)
				first = false;
			else
				result.append(delimiter);
			result.append(it.next());
		}
		return result.toString();
	}

	public static String join(Object[] array) {
		return join(array, ",");
	}

	public static String join(Object[] array, String delimiter) {
		if (array == null)
			return "";
		// Cache the length of the delimiter
		// has the side effect of throwing a NullPointerException if
		// the delimiter is null.
		int delimiterLength = delimiter.length();
		// Nothing in the array return empty string
		// has the side effect of throwing a NullPointerException if
		// the array is null.
		if (array.length == 0) {
			return "";
		}
		// Only one thing in the array, return it.
		if (array.length == 1) {
			if (array[0] == null) {
				return "";
			}
			return array[0].toString();
		}
		// Make a pass through and determine the size
		// of the resulting string.
		int length = 0;
		for (int i = 0; i < array.length; i++) {
			if (array[i] != null) {
				length += array[i].toString().length();
			}
			if (i < array.length - 1) {
				length += delimiterLength;
			}
		}
		// Make a second pass through and concatenate everything
		// into a string buffer.
		StringBuilder result = new StringBuilder(length);
		for (int i = 0; i < array.length; i++) {
			if (array[i] != null) {
				result.append(array[i].toString());
			}
			if (i < array.length - 1) {
				result.append(delimiter);
			}
		}
		return result.toString();
	}
	
	/**
	 * Convert name from 'xxxYyyyZzzz' value to 'xxxx_yyyy_zzzz' value
	 * @param name String that separated in camel form
	 * @return String that separated by underline
	 */
	public static String camelToUnderline(String name) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < name.length(); i++) {
			char ch = name.charAt(i);
			if (ch >= 'A' && ch <= 'Z') {
				if (sb.length() > 0) {
					sb.append('_');
					sb.append((char)(ch + 32));
				} else
					sb.append((char)(ch + 32));
			} else {
				sb.append(ch);
			}
		}
		return sb.toString();
	}

	public static DateTime parseISO8601(String dateTime) {
		DateTimeFormatter formatter = ISODateTimeFormat.dateTimeParser();
		return formatter.parseDateTime(dateTime);
	}

	public static String toISO8601(DateTime dateTime) {
		DateTimeFormatter formatter = ISODateTimeFormat.dateTime();
		return dateTime.toDateTimeISO().toString(formatter);
	}
	
	/**
	 * Convert name from 'xxxx_yyyy_zzzz' value to 'xxxYyyyZzzz' value
	 * @param name String that separated by underline
	 * @return String that separated in camel form
	 */
	public static String underlineToCamel(String name) {
		StringBuilder sb = new StringBuilder();
		boolean upperCaseLetter = false;
		for (int i = 0; i < name.length(); i++) {
			char ch = name.charAt(i);
			if (ch == '_') {
				upperCaseLetter = true;
			} else {
				if (upperCaseLetter && ch >= 'a' && ch <= 'z') {
					sb.append((char)(ch - 32));
				} else
					sb.append(ch);
				upperCaseLetter = false;
			}
		}
		return sb.toString();
	}

    public static String toSafeFormat(String msg) {
        if (msg == null)
            return null;
        return msg.replaceAll("'", "''").replaceAll("(\\{|\\})","'$1'");
    }

    public static String toSafeURL(String url) {
        if (url == null)
            return null;
        char[] chars = url.toCharArray();
        StringBuilder sb = new StringBuilder(url.length());
        try {
            for (char c : chars) {
                if (c > 20 && c < 127) {
                    if (c == ' ') {
                        sb.append("%20");
                    } else if (c == '<' || c == '>' || c == '\'' || c == '"') {
                        sb.append(URLEncoder.encode(c + "", "utf-8"));
                    } else
                        sb.append(c);
                } else {
                    sb.append(URLEncoder.encode(c + "", "utf-8"));
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
        return sb.toString();
    }

	/**
	 * Detect whether the string is chinese GBK garbled string.
	 * (GBK coding table: 0x8140 ~ 0xFEFE)
	 * @param str input string
	 * @return true if the string is gbk garbled or false if not.
	 */
	public static boolean isGarbledGBK(String str) {
		if (str == null)
			return false;
		boolean gbkFlag = false;
		for (char c : str.toCharArray()) {
			if (!gbkFlag) {
				if (c <= 127)
					continue;
				if (c >= 0x81 && c <= 0xFE)
					gbkFlag = true;
				else
					return false;
			} else {
				if (c >= 0x40 && c <= 0xFE)
					return true;
				else
					return false;
			}
		}
		return false;
	}

    public static boolean isUTF8(byte[] bytes) {
		int followUpSize = 0;
		if (bytes == null)
			return false;
		for (byte o : bytes) {
			int b = o & 0xFF;
			if (followUpSize == 0) {
				if ((b >> 7) == 0)
					continue;
				else if ((b >> 5) == 6) {
					followUpSize = 1;
				} else if ((b >> 4) == 14) {
					followUpSize = 2;
				} else if ((b >> 3) == 30) {
					followUpSize = 3;
				} else if ((b >> 2) == 62) {
					followUpSize = 4;
				} else if ((b >> 1) == 126) {
					followUpSize = 5;
				} else
					return false;
			} else {
				if ((b >> 6) == 2) {
					followUpSize--;
				} else
					return false;
			}
		}
		return followUpSize == 0;
	}


	public static String getErrorMessage(Throwable e) {
		if (e == null)
			return "Unknown error";
		while (e.getCause() != null) {
			e = e.getCause();
		}
		if (e.getMessage() != null)
			return e.getMessage();
		else
			return e.getClass().getName();
	}
}
